<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.9.0">
    

    

    



    <meta charset="utf-8">
    
    
    <meta name="sogou_site_verification" content="true">
    
    
    
    <title>深入学习ConcurrentHashMap | Lvshen&#39;s Blog | This is My World</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    
    <meta name="theme-color" content="#3F51B5">
    
    
    <meta name="keywords" content="ConcurrentHashMap,map">
    <meta name="baidu-site-verification" content="VIVNdSiMZm">
    <meta name="description" content="本文作者： JoonWhee 本文链接： http://opiece.me/2017/04/08/study-concurrentHashMap/  近期深入学习了ConcurrentHashMap，便整理成一篇博文记录一下，请注意：此博文针对的是JDK1.6，因此如果你看到的源码跟我文中的不同，则可能是由于版本不一样。 ConcurrentHashMap的锁分段技术HashTable容器在竞争">
<meta name="keywords" content="ConcurrentHashMap,map">
<meta property="og:type" content="article">
<meta property="og:title" content="深入学习ConcurrentHashMap">
<meta property="og:url" content="https://lvshen9.gitee.io/2017/09/14/深入学习ConcurrentHashMap/index.html">
<meta property="og:site_name" content="Lvshen&#39;s Blog">
<meta property="og:description" content="本文作者： JoonWhee 本文链接： http://opiece.me/2017/04/08/study-concurrentHashMap/  近期深入学习了ConcurrentHashMap，便整理成一篇博文记录一下，请注意：此博文针对的是JDK1.6，因此如果你看到的源码跟我文中的不同，则可能是由于版本不一样。 ConcurrentHashMap的锁分段技术HashTable容器在竞争">
<meta property="og:locale" content="zh-CN">
<meta property="og:image" content="http://opiece.me/images/article/study-concurrentHashMap-1.png">
<meta property="og:image" content="http://opiece.me/images/article/study-concurrentHashMap-2.png">
<meta property="og:updated_time" content="2017-09-14T11:25:08.757Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="深入学习ConcurrentHashMap">
<meta name="twitter:description" content="本文作者： JoonWhee 本文链接： http://opiece.me/2017/04/08/study-concurrentHashMap/  近期深入学习了ConcurrentHashMap，便整理成一篇博文记录一下，请注意：此博文针对的是JDK1.6，因此如果你看到的源码跟我文中的不同，则可能是由于版本不一样。 ConcurrentHashMap的锁分段技术HashTable容器在竞争">
<meta name="twitter:image" content="http://opiece.me/images/article/study-concurrentHashMap-1.png">
    
    <link rel="shortcut icon" href="/img/mylogo.jpg">
    <link rel="stylesheet" href="//unpkg.com/hexo-theme-material-indigo@latest/css/style.css">
    <script>window.lazyScripts=[]</script>

    <!-- custom head -->
    

</head>

<body>
    <div id="loading" class="active"></div>

    <aside id="menu" class="hide" >
  <div class="inner flex-row-vertical">
    <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menu-off">
        <i class="icon icon-lg icon-close"></i>
    </a>
    <div class="brand-wrap" style="background-image:url(/img/brand.jpg)">
      <div class="brand">
        <a href="/" class="avatar waves-effect waves-circle waves-light">
          <img src="/img/avatar.jpg">
        </a>
        <hgroup class="introduce">
          <h5 class="nickname">我的技术小房间</h5>
          <a href="mailto:https://lvshen9.github.io" title="https://lvshen9.github.io" class="mail">https://lvshen9.github.io</a>
        </hgroup>
      </div>
    </div>
    <div class="scroll-wrap flex-col">
      <ul class="nav">
        
            <li class="waves-block waves-effect">
              <a href="/"  >
                <i class="icon icon-lg icon-home"></i>
                主页
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/archives"  >
                <i class="icon icon-lg icon-archives"></i>
                Archives
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/tags"  >
                <i class="icon icon-lg icon-tags"></i>
                Tags
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/categories"  >
                <i class="icon icon-lg icon-th-list"></i>
                Categories
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/about"  >
                <i class="icon icon-lg icon-address-book"></i>
                About
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/collection"  >
                <i class="icon icon-lg icon-apple"></i>
                Collection
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="https://lvshen9.github.io/" target="_blank" >
                <i class="icon icon-lg icon-wordpress"></i>
                Blog
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="https://github.com/lvshen9" target="_blank" >
                <i class="icon icon-lg icon-github-alt"></i>
                GitHub
              </a>
            </li>
        
      </ul>
    </div>
  </div>
</aside>

    <main id="main">
        <header class="top-header" id="header">
    <div class="flex-row">
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light on" id="menu-toggle">
          <i class="icon icon-lg icon-navicon"></i>
        </a>
        <div class="flex-col header-title ellipsis">深入学习ConcurrentHashMap</div>
        
        <div class="search-wrap" id="search-wrap">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="back">
                <i class="icon icon-lg icon-chevron-left"></i>
            </a>
            <input type="text" id="key" class="search-input" autocomplete="off" placeholder="输入感兴趣的关键字">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="search">
                <i class="icon icon-lg icon-search"></i>
            </a>
        </div>
        
        
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menuShare">
            <i class="icon icon-lg icon-share-alt"></i>
        </a>
        
    </div>
</header>
<header class="content-header post-header">

    <div class="container fade-scale">
        <h1 class="title">深入学习ConcurrentHashMap</h1>
        <h5 class="subtitle">
            
                <time datetime="2017-09-14T10:22:39.000Z" itemprop="datePublished" class="page-time">
  2017-09-14
</time>


	<ul class="article-category-list"><li class="article-category-list-item"><a class="article-category-list-link" href="/categories/技术/">技术</a></li></ul>

            
        </h5>
    </div>

    


</header>


<div class="container body-wrap">
    
    <aside class="post-widget">
        <nav class="post-toc-wrap post-toc-shrink" id="post-toc">
            <h4>TOC</h4>
            <ol class="post-toc"><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#ConcurrentHashMap的锁分段技术"><span class="post-toc-number">1.</span> <span class="post-toc-text">ConcurrentHashMap的锁分段技术</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#ConcurrentHashMap的结构"><span class="post-toc-number">2.</span> <span class="post-toc-text">ConcurrentHashMap的结构</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#ConcurrentHashMap方法源码解读"><span class="post-toc-number">3.</span> <span class="post-toc-text">ConcurrentHashMap方法源码解读</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#初始化方法"><span class="post-toc-number">3.1.</span> <span class="post-toc-text">初始化方法</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#get方法"><span class="post-toc-number">3.2.</span> <span class="post-toc-text">get方法</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#put方法"><span class="post-toc-number">3.3.</span> <span class="post-toc-text">put方法</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#remove方法"><span class="post-toc-number">3.4.</span> <span class="post-toc-text">remove方法</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#replace方法"><span class="post-toc-number">3.5.</span> <span class="post-toc-text">replace方法</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#clear方法"><span class="post-toc-number">3.6.</span> <span class="post-toc-text">clear方法</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#size方法"><span class="post-toc-number">3.7.</span> <span class="post-toc-text">size方法</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#containsValue方法"><span class="post-toc-number">3.8.</span> <span class="post-toc-text">containsValue方法</span></a></li></ol></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#参考："><span class="post-toc-number">4.</span> <span class="post-toc-text">参考：</span></a></li></ol>
        </nav>
    </aside>


<article id="post-深入学习ConcurrentHashMap"
  class="post-article article-type-post fade" itemprop="blogPost">

    <div class="post-card">
        <h1 class="post-card-title">深入学习ConcurrentHashMap</h1>
        <div class="post-meta">
            <time class="post-time" title="2017-09-14 18:22:39" datetime="2017-09-14T10:22:39.000Z"  itemprop="datePublished">2017-09-14</time>

            
	<ul class="article-category-list"><li class="article-category-list-item"><a class="article-category-list-link" href="/categories/技术/">技术</a></li></ul>



            
<span id="busuanzi_container_page_pv" title="文章总阅读量" style='display:none'>
    <i class="icon icon-eye icon-pr"></i><span id="busuanzi_value_page_pv"></span>
</span>


        </div>
        <div class="post-content" id="post-content" itemprop="postContent">
            <blockquote>
<p><strong>本文作者：</strong> JoonWhee</p>
<p><strong>本文链接：</strong> <a href="http://opiece.me/2017/04/08/study-concurrentHashMap/" target="_blank" rel="noopener">http://opiece.me/2017/04/08/study-concurrentHashMap/</a></p>
</blockquote>
<p>近期深入学习了ConcurrentHashMap，便整理成一篇博文记录一下，<strong>请注意</strong>：此博文针对的是JDK1.6，因此如果你看到的源码跟我文中的不同，则可能是由于版本不一样。</p>
<h2 id="ConcurrentHashMap的锁分段技术"><a href="#ConcurrentHashMap的锁分段技术" class="headerlink" title="ConcurrentHashMap的锁分段技术"></a>ConcurrentHashMap的锁分段技术</h2><p>HashTable容器在竞争激烈的并发环境下表现出效率低下的原因，是因为所有访问HashTable的线程必须竞争同一把锁。如果容器里有多把锁，每一把锁用于锁容器的其中一部分数据，那么当多线程访问容器里不同数据段的数据时，线程间就不会存在锁竞争，从而可以有效的提高并发访问效率，这就是ConcurrentHashMap所使用的锁分段技术。首先将数据分成一段一段的存储，然后给每一段数据配一把锁，当一个线程占用锁访问其中一个段数据的时候，其他段的数据也能被其他线程访问。</p>
<h2 id="ConcurrentHashMap的结构"><a href="#ConcurrentHashMap的结构" class="headerlink" title="ConcurrentHashMap的结构"></a>ConcurrentHashMap的结构</h2><p>我们通过ConcurrentHashMap的类图来分析ConcurrentHashMap的结构。</p>
<a id="more"></a>
<p>[<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="http://opiece.me/images/article/study-concurrentHashMap-1.png" alt="img](http://opiece.me/images/article/study-concurrentHashMap-1.png)" title>
                </div>
                <div class="image-caption">img](http://opiece.me/images/article/study-concurrentHashMap-1.png)</div>
            </figure><br>ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock，在ConcurrentHashMap里扮演锁的角色，HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组，Segment的结构和HashMap类似，是一种数组和链表结构，一个Segment里包含一个HashEntry数组，每个HashEntry是一个链表结构的元素，每个Segment守护着一个HashEntry数组里的元素，当对HashEntry数组的数据进行修改时，必须首先获得它对应的Segment锁。<br>[<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="http://opiece.me/images/article/study-concurrentHashMap-2.png" alt="img](http://opiece.me/images/article/study-concurrentHashMap-2.png)" title>
                </div>
                <div class="image-caption">img](http://opiece.me/images/article/study-concurrentHashMap-2.png)</div>
            </figure></p>
<h2 id="ConcurrentHashMap方法源码解读"><a href="#ConcurrentHashMap方法源码解读" class="headerlink" title="ConcurrentHashMap方法源码解读"></a>ConcurrentHashMap方法源码解读</h2><p>请注意，如果一个方法中我贴了几段代码，那么一般是：第一段代码为方法的入口，其他的为被入口方法调用过的方法。</p>
<h3 id="初始化方法"><a href="#初始化方法" class="headerlink" title="初始化方法"></a>初始化方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ConcurrentHashMap</span><span class="params">(<span class="keyword">int</span> initialCapacity,</span></span></span><br><span class="line"><span class="function"><span class="params">                         <span class="keyword">float</span> loadFactor, <span class="keyword">int</span> concurrencyLevel)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!(loadFactor &gt; <span class="number">0</span>) || initialCapacity &lt; <span class="number">0</span> || concurrencyLevel &lt;= <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (concurrencyLevel &gt; MAX_SEGMENTS)</span><br><span class="line">        concurrencyLevel = MAX_SEGMENTS;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Find power-of-two sizes best matching arguments</span></span><br><span class="line">    <span class="keyword">int</span> sshift = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> ssize = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (ssize &lt; concurrencyLevel) &#123;</span><br><span class="line">        ++sshift;</span><br><span class="line">        ssize &lt;&lt;= <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    segmentShift = <span class="number">32</span> - sshift;</span><br><span class="line">    segmentMask = ssize - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">this</span>.segments = Segment.newArray(ssize);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (initialCapacity &gt; MAXIMUM_CAPACITY)</span><br><span class="line">        initialCapacity = MAXIMUM_CAPACITY;</span><br><span class="line">    <span class="keyword">int</span> c = initialCapacity / ssize;</span><br><span class="line">    <span class="keyword">if</span> (c * ssize &lt; initialCapacity)</span><br><span class="line">        ++c;</span><br><span class="line">    <span class="keyword">int</span> cap = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (cap &lt; c)</span><br><span class="line">        cap &lt;&lt;= <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="keyword">this</span>.segments.length; ++i)</span><br><span class="line">        <span class="keyword">this</span>.segments[i] = <span class="keyword">new</span> Segment&lt;K,V&gt;(cap, loadFactor);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>代码中的第一个while循环是用来计算segments数组的大小ssize（必须为2的N次方）。segmentShift和segmentMask是用来定位当前元素在哪个segment，前者用于移位，后者用于进行位与运算。第二个while循环是用来计算每个segment中HashEntry数组的大小cap（必须为2的N次方），最后对segments数组进行初始化。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Segment(<span class="keyword">int</span> initialCapacity, <span class="keyword">float</span> lf) &#123;</span><br><span class="line">    loadFactor = lf;</span><br><span class="line">    setTable(HashEntry.&lt;K,V&gt;newArray(initialCapacity));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">setTable</span><span class="params">(HashEntry&lt;K,V&gt;[] newTable)</span> </span>&#123;</span><br><span class="line">    threshold = (<span class="keyword">int</span>)(newTable.length * loadFactor);</span><br><span class="line">    table = newTable;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> &lt;K,V&gt; HashEntry&lt;K,V&gt;[] newArray(<span class="keyword">int</span> i) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> HashEntry[i];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>对segments数组进行初始化的同时，也对segment类里面的HashEntry进行初始化，并给loadFactor和threshold赋值。</p>
<h3 id="get方法"><a href="#get方法" class="headerlink" title="get方法"></a>get方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">get</span><span class="params">(Object key)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> hash = hash(key.hashCode());</span><br><span class="line">    <span class="keyword">return</span> segmentFor(hash).get(key, hash);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>根据key的hashcode重新计算hash值（主要是为了减少hash冲突），通过segmentFor方法定位到具体的哪个segment，然后调用segment的get方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">final</span> Segment&lt;K,V&gt; <span class="title">segmentFor</span><span class="params">(<span class="keyword">int</span> hash)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> segments[(hash &gt;&gt;&gt; segmentShift) &amp; segmentMask];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>segmentFor方法是用来定位到具体的segment的，主要是通过使用hash值的高位与掩码进行位运算，segmentShift和segmentMask是通过上文初始化方法计算而来。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">V <span class="title">get</span><span class="params">(Object key, <span class="keyword">int</span> hash)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (count != <span class="number">0</span>) &#123; <span class="comment">// read-volatile</span></span><br><span class="line">        HashEntry&lt;K,V&gt; e = getFirst(hash);</span><br><span class="line">        <span class="keyword">while</span> (e != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (e.hash == hash &amp;&amp; key.equals(e.key)) &#123;</span><br><span class="line">                V v = e.value;</span><br><span class="line">                <span class="keyword">if</span> (v != <span class="keyword">null</span>)</span><br><span class="line">                    <span class="keyword">return</span> v;</span><br><span class="line">                <span class="keyword">return</span> readValueUnderLock(e); <span class="comment">// recheck</span></span><br><span class="line">            &#125;</span><br><span class="line">            e = e.next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">HashEntry&lt;K,V&gt; <span class="title">getFirst</span><span class="params">(<span class="keyword">int</span> hash)</span> </span>&#123;</span><br><span class="line">    HashEntry&lt;K,V&gt;[] tab = table;</span><br><span class="line">    <span class="keyword">return</span> tab[hash &amp; (tab.length - <span class="number">1</span>)];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>根据hash值跟数组长度-1进行位与运算，定位到具体的HashEntry（getFirst方法），遍历该HashEntry链表，找到链表中某个元素的hash值与传入的hash值相同并且使用equals方法比较key相同的元素，如果该元素的value不为空，返回value值；如果为空，则尝试在加锁的情况下再读一次。<br>get操作的高效之处在于整个get过程不需要加锁，除非读到的值是空的才会加锁重读，我们知道HashTable容器的get方法是需要加锁的，那么ConcurrentHashMap的get操作是如何做到不加锁的呢？原因是它的get方法里将要使用的共享变量都定义成volatile，如用于统计当前Segement大小的count字段和用于存储值的HashEntry的value，定义成volatile的变量，能够在线程之间保持可见性，能够被多线程同时读，并且保证不会读到过期的值，但是只能被单线程写（有一种情况可以被多线程写，就是写入的值不依赖于原值），在get操作里只需要读不需要写共享变量count和value，所以可以不用加锁。之所以不会读到过期的值，是根据Java内存模型的happen before原则，对volatile字段的写入操作先于读操作，即使两个线程同时修改和获取volatile变量，get操作也能拿到最新的值，这是用volatile替换锁的经典应用场景。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Reads value field of an entry under lock. Called if value</span></span><br><span class="line"><span class="comment"> * field ever appears to be null. This is possible only if a</span></span><br><span class="line"><span class="comment"> * compiler happens to reorder a HashEntry initialization with</span></span><br><span class="line"><span class="comment"> * its table assignment, which is legal under memory model</span></span><br><span class="line"><span class="comment"> * but is not known to ever occur.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function">V <span class="title">readValueUnderLock</span><span class="params">(HashEntry&lt;K,V&gt; e)</span> </span>&#123;</span><br><span class="line">    lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> e.value;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>readValueUnderLock：在有锁的状态下再读一次。这似乎有些费解，理论上结点的值不可能为空，这是因为put的时候就进行了判断，如果为空就要抛NullPointerException。空值的唯一源头就是HashEntry中的默认值，因为HashEntry中的value不是final的，非同步读取有可能读取到空值。仔细看下put操作的语句：tab[index] = new HashEntry(key, hash, first, value)，在这条语句中，HashEntry构造函数中对value的赋值以及对tab[index]的赋值可能被重新排序（方法上面的一大段注释有提到，英语好的可以直接读注释），这就可能导致结点的值为空。这里当value为空时，可能是一个线程正在改变节点，而之前的get操作都未进行锁定，根据bernstein条件，读后写或写后读都会引起数据的不一致，所以这里要对这个e重新上锁再读一遍，以保证得到的是正确值。</p>
<h3 id="put方法"><a href="#put方法" class="headerlink" title="put方法"></a>put方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (value == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">    <span class="keyword">int</span> hash = hash(key.hashCode());</span><br><span class="line">    <span class="keyword">return</span> segmentFor(hash).put(key, hash, value, <span class="keyword">false</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>根据key的hashcode重新计算hash值（跟get方法一样），通过segmentFor方法定位到具体的哪个segment，然后调用segment的put方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">V <span class="title">put</span><span class="params">(K key, <span class="keyword">int</span> hash, V value, <span class="keyword">boolean</span> onlyIfAbsent)</span> </span>&#123;</span><br><span class="line">    lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">int</span> c = count;</span><br><span class="line">        <span class="keyword">if</span> (c++ &gt; threshold) <span class="comment">// ensure capacity</span></span><br><span class="line">            rehash();</span><br><span class="line">        HashEntry&lt;K,V&gt;[] tab = table;</span><br><span class="line">        <span class="keyword">int</span> index = hash &amp; (tab.length - <span class="number">1</span>);</span><br><span class="line">        HashEntry&lt;K,V&gt; first = tab[index];</span><br><span class="line">        HashEntry&lt;K,V&gt; e = first;</span><br><span class="line">        <span class="keyword">while</span> (e != <span class="keyword">null</span> &amp;&amp; (e.hash != hash || !key.equals(e.key)))</span><br><span class="line">            e = e.next;</span><br><span class="line"></span><br><span class="line">        V oldValue;</span><br><span class="line">        <span class="keyword">if</span> (e != <span class="keyword">null</span>) &#123;</span><br><span class="line">            oldValue = e.value;</span><br><span class="line">            <span class="keyword">if</span> (!onlyIfAbsent)</span><br><span class="line">                e.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            oldValue = <span class="keyword">null</span>;</span><br><span class="line">            ++modCount;</span><br><span class="line">            tab[index] = <span class="keyword">new</span> HashEntry&lt;K,V&gt;(key, hash, first, value);</span><br><span class="line">            count = c; <span class="comment">// write-volatile</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> oldValue;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>加锁进行以下操作：判断是否需要扩容，如果需要则调用rehash方法（下面有介绍）。根据hash值跟数组长度-1进行位与运算，定位到具体的HashEntry，遍历该HashEntry，根据传入的的key，使用equals方法找到需要的元素。如果能找到，则将该元素的value值覆盖为传入的value，否则将传入的key、value、hash值作为一个新元素放在该HashEntry的头部，最后进行解锁。入参中的onlyIfAbsent为true时，表示如果该key已经存在value值，则不会覆盖原value值。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">rehash</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    HashEntry&lt;K,V&gt;[] oldTable = table;</span><br><span class="line">    <span class="keyword">int</span> oldCapacity = oldTable.length;</span><br><span class="line">    <span class="keyword">if</span> (oldCapacity &gt;= MAXIMUM_CAPACITY)</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * Reclassify nodes in each list to new Map.  Because we are</span></span><br><span class="line"><span class="comment">     * using power-of-two expansion, the elements from each bin</span></span><br><span class="line"><span class="comment">     * must either stay at same index, or move with a power of two</span></span><br><span class="line"><span class="comment">     * offset. We eliminate unnecessary node creation by catching</span></span><br><span class="line"><span class="comment">     * cases where old nodes can be reused because their next</span></span><br><span class="line"><span class="comment">     * fields won't change. Statistically, at the default</span></span><br><span class="line"><span class="comment">     * threshold, only about one-sixth of them need cloning when</span></span><br><span class="line"><span class="comment">     * a table doubles. The nodes they replace will be garbage</span></span><br><span class="line"><span class="comment">     * collectable as soon as they are no longer referenced by any</span></span><br><span class="line"><span class="comment">     * reader thread that may be in the midst of traversing table</span></span><br><span class="line"><span class="comment">     * right now.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"></span><br><span class="line">    HashEntry&lt;K,V&gt;[] newTable = HashEntry.newArray(oldCapacity&lt;&lt;<span class="number">1</span>);	<span class="comment">//新表扩容为原来大小的2倍</span></span><br><span class="line">    threshold = (<span class="keyword">int</span>)(newTable.length * loadFactor);	<span class="comment">//重新计算阀值</span></span><br><span class="line">    <span class="keyword">int</span> sizeMask = newTable.length - <span class="number">1</span>;		<span class="comment">//新表的掩码值还是为表长度-1</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; oldCapacity ; i++) &#123;	</span><br><span class="line">        <span class="comment">// We need to guarantee that any existing reads of old Map can</span></span><br><span class="line">        <span class="comment">//  proceed. So we cannot yet null out each bin.</span></span><br><span class="line">        HashEntry&lt;K,V&gt; e = oldTable[i];</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (e != <span class="keyword">null</span>) &#123;</span><br><span class="line">            HashEntry&lt;K,V&gt; next = e.next;	<span class="comment">//元素e的下一个元素</span></span><br><span class="line">            <span class="keyword">int</span> idx = e.hash &amp; sizeMask;	<span class="comment">//计算元素e在新表中的索引位位置</span></span><br><span class="line"></span><br><span class="line">            <span class="comment">//  Single node on list</span></span><br><span class="line">            <span class="keyword">if</span> (next == <span class="keyword">null</span>)	<span class="comment">//如果当前位置只有一个元素，则直接移动到新表的对应位置</span></span><br><span class="line">                newTable[idx] = e;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// Reuse trailing consecutive sequence at same slot</span></span><br><span class="line">                HashEntry&lt;K,V&gt; lastRun = e;	<span class="comment">//lastRun：最后一个需要处理的元素，初始值为元素e</span></span><br><span class="line">                <span class="keyword">int</span> lastIdx = idx;	<span class="comment">//lastIdx：最后一个需要处理的元素的索引位置，初始值为元素e在新表中的索引值</span></span><br><span class="line">                <span class="keyword">for</span> (HashEntry&lt;K,V&gt; last = next;	<span class="comment">//遍历该链表，找到最后一个需要处理的元素</span></span><br><span class="line">                     last != <span class="keyword">null</span>;</span><br><span class="line">                     last = last.next) &#123;</span><br><span class="line">                    <span class="keyword">int</span> k = last.hash &amp; sizeMask;</span><br><span class="line">                    <span class="keyword">if</span> (k != lastIdx) &#123;	<span class="comment">//如果当前元素的索引位置跟lastIdx不一致，则将lastIdx和lastRun替换成当前元素的相应值</span></span><br><span class="line">                        lastIdx = k;</span><br><span class="line">                        lastRun = last;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                newTable[lastIdx] = lastRun;	<span class="comment">//将最后一个需要处理的元素放到新表中</span></span><br><span class="line"></span><br><span class="line">                <span class="comment">// Clone all remaining nodes</span></span><br><span class="line">                <span class="keyword">for</span> (HashEntry&lt;K,V&gt; p = e; p != lastRun; p = p.next) &#123;<span class="comment">//遍历处理lastRun之前的所有元素</span></span><br><span class="line">                    <span class="keyword">int</span> k = p.hash &amp; sizeMask;	<span class="comment">//计算当前遍历元素p在新表的索引k</span></span><br><span class="line">                    HashEntry&lt;K,V&gt; n = newTable[k];	<span class="comment">//取到新表中索引位置k的链表头元素赋值给n</span></span><br><span class="line">                    newTable[k] = <span class="keyword">new</span> HashEntry&lt;K,V&gt;(p.key, p.hash,</span><br><span class="line">                                                     n, p.value);	<span class="comment">//将当前遍历元素p复制到新表的索引位置k的链表头部，next属性指向新表该索引位置原来的链表头n</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    table = newTable;	<span class="comment">//将新表赋值给table</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>lastRun：最后一个需要处理的元素的意思就是该元素之后的所有元素都跟该元素有相同的索引值(对于新表)，所以只需要将该元素放到新表的对应位置，该元素之后的所有元素也就跟着到了新表的对应位置。相当于直接将该链表的最后一截（可能包含若干个元素）直接一次性移到了新表的某个位置。<br>如果整个循环结束，if (k != lastIdx) 语句没有成立过，就代表当前位置（oldTable[i]）的整个HashEntry在新表中的索引位置是一致的，只需要移动一次即可将整个链表移到新表上。根据rehash方法中的那一大段注释提到的“ Statistically, at the default threshold, only about one-sixth of them need cloning when a table doubles”（据统计，在默认阈值下，当表扩大为原来的两倍时，只有约六分之一的元素需要克隆），可以想象，这个if语句没有成立过的可能性应该是挺大的。</p>
<h3 id="remove方法"><a href="#remove方法" class="headerlink" title="remove方法"></a>remove方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">remove</span><span class="params">(Object key)</span> </span>&#123;</span><br><span class="line"><span class="keyword">int</span> hash = hash(key.hashCode());</span><br><span class="line">    <span class="keyword">return</span> segmentFor(hash).remove(key, hash, <span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>根据key的hashcode重新计算hash值（跟get方法一样），通过segmentFor方法定位到具体的哪个segment，然后调用segment的remove方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">V <span class="title">remove</span><span class="params">(Object key, <span class="keyword">int</span> hash, Object value)</span> </span>&#123;</span><br><span class="line">    lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">int</span> c = count - <span class="number">1</span>;</span><br><span class="line">        HashEntry&lt;K,V&gt;[] tab = table;</span><br><span class="line">        <span class="keyword">int</span> index = hash &amp; (tab.length - <span class="number">1</span>);</span><br><span class="line">        HashEntry&lt;K,V&gt; first = tab[index];</span><br><span class="line">        HashEntry&lt;K,V&gt; e = first;</span><br><span class="line">        <span class="keyword">while</span> (e != <span class="keyword">null</span> &amp;&amp; (e.hash != hash || !key.equals(e.key)))</span><br><span class="line">            e = e.next;</span><br><span class="line"></span><br><span class="line">        V oldValue = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (e != <span class="keyword">null</span>) &#123;</span><br><span class="line">            V v = e.value;</span><br><span class="line">            <span class="keyword">if</span> (value == <span class="keyword">null</span> || value.equals(v)) &#123;</span><br><span class="line">                oldValue = v;</span><br><span class="line">                <span class="comment">// All entries following removed node can stay</span></span><br><span class="line">                <span class="comment">// in list, but all preceding ones need to be</span></span><br><span class="line">                <span class="comment">// cloned.</span></span><br><span class="line">                ++modCount;</span><br><span class="line">                HashEntry&lt;K,V&gt; newFirst = e.next;</span><br><span class="line">                <span class="keyword">for</span> (HashEntry&lt;K,V&gt; p = first; p != e; p = p.next)</span><br><span class="line">                    newFirst = <span class="keyword">new</span> HashEntry&lt;K,V&gt;(p.key, p.hash,</span><br><span class="line">                                                  newFirst, p.value);</span><br><span class="line">                tab[index] = newFirst;</span><br><span class="line">                count = c; <span class="comment">// write-volatile</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> oldValue;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>加锁进行以下操作：根据hash值跟数组长度-1进行位运算,定位到具体的HashEntry，遍历该HashEntry，根据传入的的key，使用equals方法找到需要的元素，进行以下操作。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">HashEntry&lt;K,V&gt; newFirst = e.next;</span><br><span class="line"><span class="keyword">for</span> (HashEntry&lt;K,V&gt; p = first; p != e; p = p.next)</span><br><span class="line">    newFirst = <span class="keyword">new</span> HashEntry&lt;K,V&gt;(p.key, p.hash,</span><br><span class="line">                                  newFirst, p.value);</span><br><span class="line">tab[index] = newFirst;</span><br></pre></td></tr></table></figure>
<p>该段代码是remove方法中的片段，过程比较特殊，拿出来单独讨论。因为HashEntry使用final修饰，这意味着在第一次设置了next域之后便不能再改变它，因此，此处的remove操作是新建一个HashEntry并将它之前的节点全都克隆一次。至于HashEntry为什么要设置为不变性，这跟不变性的访问不需要同步从而节省时间有关。<br>用实际例子看上面这段代码更容易懂：<br>假设1：此时HashEntry为：1 2 3 4 5 6,其中1为链表头,并且1.next = 2，2.next = 3以此类推。<br>假设2：此时e = 4，即根据key匹配到的元素4是即将remove掉的。<br>则上面这段代码有以下流程：<br>HashEntry newFirst = 4.next = 5<br>for( p = 1; p != 4; p++)<br>newFirst = new HashEntry(p.key, p.hash, newFirst, p.value);<br>此循环如下：<br>p = 1：newFirst = new HashEntry(1.key, 1.hash, 5, 1.value)<br>p = 2：newFirst = new HashEntry(2.key, 2.hash, 1, 2.value)<br>p = 3：newFirst = new HashEntry(3.key, 3.hash, 2, 3.value)<br>p = 4：结束循环<br>tab[index] = 3;<br>index为当前链表在HashEntry中的索引位置，所以此时HashEntry为：3 2 1 5 6，被remove的元素之前的元素顺序颠倒了。</p>
<p>remove方法中还有以下这句代码，这句代码在代码中出现非常多次，主要是起什么作用？</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HashEntry&lt;K,V&gt;[] tab = table;</span><br></pre></td></tr></table></figure>
<p>这句代码是将table赋给一个局部变量tab，这是因为table是 volatile变量，读写volatile变量的开销很大，编译器也不能对volatile变量的读写做任何优化，直接多次访问非volatile实例变量没有多大影响，编译器会做相应优化。</p>
<h3 id="replace方法"><a href="#replace方法" class="headerlink" title="replace方法"></a>replace方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">replace</span><span class="params">(K key, V oldValue, V newValue)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (oldValue == <span class="keyword">null</span> || newValue == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">    <span class="keyword">int</span> hash = hash(key.hashCode());</span><br><span class="line">    <span class="keyword">return</span> segmentFor(hash).replace(key, hash, oldValue, newValue);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>根据key的hashcode重新计算hash值（跟get方法一样），通过segmentFor方法定位到具体的哪个segment，然后调用segment的replace方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">boolean</span> <span class="title">replace</span><span class="params">(K key, <span class="keyword">int</span> hash, V oldValue, V newValue)</span> </span>&#123;</span><br><span class="line">    lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        HashEntry&lt;K,V&gt; e = getFirst(hash);</span><br><span class="line">        <span class="keyword">while</span> (e != <span class="keyword">null</span> &amp;&amp; (e.hash != hash || !key.equals(e.key)))</span><br><span class="line">            e = e.next;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">boolean</span> replaced = <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (e != <span class="keyword">null</span> &amp;&amp; oldValue.equals(e.value)) &#123;</span><br><span class="line">            replaced = <span class="keyword">true</span>;</span><br><span class="line">            e.value = newValue;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> replaced;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>加锁进行以下操作：根据hash值跟数组长度-1进行位运算，定位到具体的HashEntry（getFirst方法），遍历该HashEntry，使用equals方法比较传入的key和链表中元素中的key，找到所需元素。如果能找到并且该元素的value跟传入的oldValue相等，则将该元素的value替换成newValue。</p>
<h3 id="clear方法"><a href="#clear方法" class="headerlink" title="clear方法"></a>clear方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; segments.length; ++i)</span><br><span class="line">        segments[i].clear();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (count != <span class="number">0</span>) &#123;</span><br><span class="line">        lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            HashEntry&lt;K,V&gt;[] tab = table;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; tab.length ; i++)</span><br><span class="line">                tab[i] = <span class="keyword">null</span>;</span><br><span class="line">            ++modCount;</span><br><span class="line">            count = <span class="number">0</span>; <span class="comment">// write-volatile</span></span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>遍历segments，对每一个segment进行清空操作：加锁进行以下操作，遍历HashEntry数组，将每个HashEntry设置为null，并将count设置为0。</p>
<h3 id="size方法"><a href="#size方法" class="headerlink" title="size方法"></a>size方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> Segment&lt;K,V&gt;[] segments = <span class="keyword">this</span>.segments;</span><br><span class="line">    <span class="keyword">long</span> sum = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">long</span> check = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span>[] mc = <span class="keyword">new</span> <span class="keyword">int</span>[segments.length];</span><br><span class="line">    <span class="comment">// Try a few times to get accurate count. On failure due to</span></span><br><span class="line">    <span class="comment">// continuous async changes in table, resort to locking.</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> k = <span class="number">0</span>; k &lt; RETRIES_BEFORE_LOCK; ++k) &#123;</span><br><span class="line">        check = <span class="number">0</span>;</span><br><span class="line">        sum = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">int</span> mcsum = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; segments.length; ++i) &#123;<span class="comment">//第一次统计</span></span><br><span class="line">            sum += segments[i].count;</span><br><span class="line">            mcsum += mc[i] = segments[i].modCount;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (mcsum != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; segments.length; ++i) &#123;<span class="comment">//第二次统计</span></span><br><span class="line">                check += segments[i].count;</span><br><span class="line">                <span class="keyword">if</span> (mc[i] != segments[i].modCount) &#123;<span class="comment">//modCount发生该变则结束当次尝试</span></span><br><span class="line">                    check = -<span class="number">1</span>; <span class="comment">// force retry</span></span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (check == sum)</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (check != sum) &#123; <span class="comment">// Resort to locking all segments</span></span><br><span class="line">        sum = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; segments.length; ++i)</span><br><span class="line">            segments[i].lock();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; segments.length; ++i)</span><br><span class="line">            sum += segments[i].count;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; segments.length; ++i)</span><br><span class="line">            segments[i].unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (sum &gt; Integer.MAX_VALUE)</span><br><span class="line">        <span class="keyword">return</span> Integer.MAX_VALUE;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>)sum;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>先在不加锁的情况下尝试进行统计，如果两次统计结果相同，并且两次统计之间没有任何对segment的修改操作（即每个segment的modCount没有改变），则返回统计结果。否则，对每个segment进行加锁，然后统计出结果，返回结果。</p>
<h3 id="containsValue方法"><a href="#containsValue方法" class="headerlink" title="containsValue方法"></a>containsValue方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">containsValue</span><span class="params">(Object value)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (value == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// See explanation of modCount use above</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> Segment&lt;K,V&gt;[] segments = <span class="keyword">this</span>.segments;</span><br><span class="line">    <span class="keyword">int</span>[] mc = <span class="keyword">new</span> <span class="keyword">int</span>[segments.length];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Try a few times without locking</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> k = <span class="number">0</span>; k &lt; RETRIES_BEFORE_LOCK; ++k) &#123;</span><br><span class="line">        <span class="keyword">int</span> sum = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">int</span> mcsum = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; segments.length; ++i) &#123;</span><br><span class="line">            <span class="keyword">int</span> c = segments[i].count;</span><br><span class="line">            mcsum += mc[i] = segments[i].modCount;</span><br><span class="line">            <span class="keyword">if</span> (segments[i].containsValue(value))<span class="comment">//遍历该segment里面的所有HashEntry的所有元素</span></span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">boolean</span> cleanSweep = <span class="keyword">true</span>;</span><br><span class="line">        <span class="keyword">if</span> (mcsum != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; segments.length; ++i) &#123;</span><br><span class="line">                <span class="keyword">int</span> c = segments[i].count;</span><br><span class="line">                <span class="keyword">if</span> (mc[i] != segments[i].modCount) &#123;<span class="comment">//如果modCount发生改变则结束尝试，进行加锁操作</span></span><br><span class="line">                    cleanSweep = <span class="keyword">false</span>;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (cleanSweep)	<span class="comment">//cleanSweep为true表示所有segment的modCount没有发生过改变</span></span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// Resort to locking all segments</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; segments.length; ++i)</span><br><span class="line">        segments[i].lock();	<span class="comment">//对所有segment进行加锁</span></span><br><span class="line">    <span class="keyword">boolean</span> found = <span class="keyword">false</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; segments.length; ++i) &#123;</span><br><span class="line">            <span class="keyword">if</span> (segments[i].containsValue(value)) &#123;<span class="comment">//遍历该segment里面的所有HashEntry的所有元素</span></span><br><span class="line">                found = <span class="keyword">true</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; segments.length; ++i)</span><br><span class="line">            segments[i].unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> found;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">boolean</span> <span class="title">containsValue</span><span class="params">(Object value)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (count != <span class="number">0</span>) &#123; <span class="comment">// read-volatile</span></span><br><span class="line">        HashEntry&lt;K,V&gt;[] tab = table;</span><br><span class="line">        <span class="keyword">int</span> len = tab.length;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span> ; i &lt; len; i++) &#123;	<span class="comment">//遍历所有HashEntry</span></span><br><span class="line">            <span class="keyword">for</span> (HashEntry&lt;K,V&gt; e = tab[i]; e != <span class="keyword">null</span>; e = e.next) &#123;	<span class="comment">//遍历每个HashEntry的所有元素</span></span><br><span class="line">                V v = e.value;</span><br><span class="line">                <span class="keyword">if</span> (v == <span class="keyword">null</span>) <span class="comment">// recheck</span></span><br><span class="line">                    v = readValueUnderLock(e);</span><br><span class="line">                <span class="keyword">if</span> (value.equals(v))</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>先在不加锁的情况下尝试进行查找，遍历所有segment的所有HashEntry的所有元素，如果找到则返回true，如果找不到且在遍历期间没有任何对segment的修改操作（即每个segment的modCount没有改变）则返回false。如果在遍历期间segment进行过修改操作，则结束不加锁的尝试。循环对每个segment进行加锁，然后进行遍历查找是否存在。</p>
<h2 id="参考："><a href="#参考：" class="headerlink" title="参考："></a>参考：</h2><p>JDK1.6源码<br><a href="http://www.cnblogs.com/ITtangtang/p/3948786.html" target="_blank" rel="noopener">Java集合—-ConcurrentHashMap原理分析</a></p>

        </div>

        <blockquote class="post-copyright">
    
    <div class="content">
        
<span class="post-time">
    最后更新时间：<time datetime="2017-09-14T11:25:08.757Z" itemprop="dateUpdated">2017-09-14 19:25:08</time>
</span><br>


        
        原文链接：<a href="/2017/09/14/深入学习ConcurrentHashMap/" target="_blank" rel="external">https://lvshen9.gitee.io/2017/09/14/深入学习ConcurrentHashMap/</a>
        
    </div>
    
    <footer>
        <a href="https://lvshen9.gitee.io">
            <img src="/img/avatar.jpg" alt="我的技术小房间">
            我的技术小房间
        </a>
    </footer>
</blockquote>

        
<div class="page-reward">
    <a id="rewardBtn" href="javascript:;" class="page-reward-btn waves-effect waves-circle waves-light">赏</a>
</div>



        <div class="post-footer">
            
	<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/ConcurrentHashMap/">ConcurrentHashMap</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/map/">map</a></li></ul>


            
<div class="page-share-wrap">
    

<div class="page-share" id="pageShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=https://lvshen9.gitee.io/2017/09/14/深入学习ConcurrentHashMap/&title=《深入学习ConcurrentHashMap》 — Lvshen's Blog&pic=https://lvshen9.gitee.io/img/avatar.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=https://lvshen9.gitee.io/2017/09/14/深入学习ConcurrentHashMap/&title=《深入学习ConcurrentHashMap》 — Lvshen's Blog&source=
本文作者： JoonWhee
本文链接： http://opiece.me/2017/04/08/study-concurrentHashMap/

近..." data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=https://lvshen9.gitee.io/2017/09/14/深入学习ConcurrentHashMap/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《深入学习ConcurrentHashMap》 — Lvshen's Blog&url=https://lvshen9.gitee.io/2017/09/14/深入学习ConcurrentHashMap/&via=https://lvshen9.gitee.io" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=https://lvshen9.gitee.io/2017/09/14/深入学习ConcurrentHashMap/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>



    <a href="javascript:;" id="shareFab" class="page-share-fab waves-effect waves-circle">
        <i class="icon icon-share-alt icon-lg"></i>
    </a>
</div>



        </div>
    </div>

    
<nav class="post-nav flex-row flex-justify-between">
  
    <div class="waves-block waves-effect prev">
      <a href="/2017/09/15/Redis对象类型底层简介/" id="post-prev" class="post-nav-link">
        <div class="tips"><i class="icon icon-angle-left icon-lg icon-pr"></i> Prev</div>
        <h4 class="title">Redis对象类型底层简介</h4>
      </a>
    </div>
  

  
    <div class="waves-block waves-effect next">
      <a href="/2017/09/13/解析ConcurrentHashMapp/" id="post-next" class="post-nav-link">
        <div class="tips">Next <i class="icon icon-angle-right icon-lg icon-pl"></i></div>
        <h4 class="title">解析ConcurrentHashMapp</h4>
      </a>
    </div>
  
</nav>



    











    <!-- Valine Comments -->
    <div class="comments vcomment" id="comments"></div>
    <script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
    <script src="//unpkg.com/valine@latest/dist/Valine.min.js"></script>
    <!-- Valine Comments script -->
    <script>
        var GUEST_INFO = ['nick','mail','link'];
        var guest_info = 'nick,mail,link'.split(',').filter(function(item){
          return GUEST_INFO.indexOf(item) > -1
        });
        new Valine({
            el: '#comments',
            notify: 'false' == 'true',
            verify: 'false' == 'true',
            appId: "dy9kXHwg5jQUlLryQmpjWRlM-gzGzoHsz",
            appKey: "P9Nh39Ol0JbMMiYqNGHEP3ml",
            avatar: "mm",
            placeholder: "Just go go",
            guest_info: guest_info.length == 0 ? GUEST_INFO : guest_info,
            pageSize: "10"
        })
    </script>
    <!-- Valine Comments end -->







</article>

<div id="reward" class="page-modal reward-lay">
    <a class="close" href="javascript:;"><i class="icon icon-close"></i></a>
    <h3 class="reward-title">
        <i class="icon icon-quote-left"></i>
        谢谢大爷~
        <i class="icon icon-quote-right"></i>
    </h3>
    <div class="reward-content">
        
        <div class="reward-code">
            <img id="rewardCode" src="https://lvshen9.github.io/blog2/pay/weixin.jpg" alt="打赏二维码">
        </div>
        
        <label class="reward-toggle">
            <input id="rewardToggle" type="checkbox" class="reward-toggle-check"
                data-wechat="https://lvshen9.github.io/blog2/pay/weixin.jpg" data-alipay="https://lvshen9.github.io/blog2/pay/zhifu.jpg">
            <div class="reward-toggle-ctrol">
                <span class="reward-toggle-item wechat">微信</span>
                <span class="reward-toggle-label"></span>
                <span class="reward-toggle-item alipay">支付宝</span>
            </div>
        </label>
        
    </div>
</div>



</div>

        <footer class="footer">
    <div class="top">
        
<p>
    <span id="busuanzi_container_site_uv" style='display:none'>
        站点总访客数：<span id="busuanzi_value_site_uv"></span>
    </span>
    <span id="busuanzi_container_site_pv" style='display:none'>
        站点总访问量：<span id="busuanzi_value_site_pv"></span>
    </span>
</p>


        <p>
            
            <span>博客内容遵循 <a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">知识共享 署名 - 非商业性 - 相同方式共享 4.0 国际协议</a></span>
        </p>
    </div>
    <div class="bottom">
        <p><span>我的技术小房间 &copy; 2015 - 2020</span>
            <span>
                
                Power by <a href="http://hexo.io/" target="_blank">Hexo</a> Theme <a href="https://github.com/yscoder/hexo-theme-indigo" target="_blank">indigo</a>
            </span>
        </p>
    </div>
</footer>

    </main>
    <div class="mask" id="mask"></div>
<a href="javascript:;" id="gotop" class="waves-effect waves-circle waves-light"><span class="icon icon-lg icon-chevron-up"></span></a>



<div class="global-share" id="globalShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=https://lvshen9.gitee.io/2017/09/14/深入学习ConcurrentHashMap/&title=《深入学习ConcurrentHashMap》 — Lvshen's Blog&pic=https://lvshen9.gitee.io/img/avatar.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=https://lvshen9.gitee.io/2017/09/14/深入学习ConcurrentHashMap/&title=《深入学习ConcurrentHashMap》 — Lvshen's Blog&source=
本文作者： JoonWhee
本文链接： http://opiece.me/2017/04/08/study-concurrentHashMap/

近..." data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=https://lvshen9.gitee.io/2017/09/14/深入学习ConcurrentHashMap/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《深入学习ConcurrentHashMap》 — Lvshen's Blog&url=https://lvshen9.gitee.io/2017/09/14/深入学习ConcurrentHashMap/&via=https://lvshen9.gitee.io" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=https://lvshen9.gitee.io/2017/09/14/深入学习ConcurrentHashMap/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>


<div class="page-modal wx-share" id="wxShare">
    <a class="close" href="javascript:;"><i class="icon icon-close"></i></a>
    <p>扫一扫，分享到微信</p>
    <img src="//api.qrserver.com/v1/create-qr-code/?data=https://lvshen9.gitee.io/2017/09/14/深入学习ConcurrentHashMap/" alt="微信分享二维码">
</div>




    <script src="//cdn.bootcss.com/node-waves/0.7.4/waves.min.js"></script>
<script>
var BLOG = { ROOT: '/', SHARE: true, REWARD: true };


</script>

<script src="//unpkg.com/hexo-theme-material-indigo@latest/js/main.min.js"></script>


<div class="search-panel" id="search-panel">
    <ul class="search-result" id="search-result"></ul>
</div>
<template id="search-tpl">
<li class="item">
    <a href="{path}" class="waves-block waves-effect">
        <div class="title ellipsis" title="{title}">{title}</div>
        <div class="flex-row flex-middle">
            <div class="tags ellipsis">
                {tags}
            </div>
            <time class="flex-col time">{date}</time>
        </div>
    </a>
</li>
</template>

<script src="//unpkg.com/hexo-theme-material-indigo@latest/js/search.min.js" async></script>






<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>



<script>
(function() {
    var OriginTitile = document.title, titleTime;
    document.addEventListener('visibilitychange', function() {
        if (document.hidden) {
            document.title = '死鬼去哪里了！';
            clearTimeout(titleTime);
        } else {
            document.title = '(つェ⊂)咦!又好了!';
            titleTime = setTimeout(function() {
                document.title = OriginTitile;
            },2000);
        }
    });
})();
</script>



</body>
</html>
